/**
 * Copyright (c) 2011-2013, James Zhan 詹波 (jfinal@126.com).
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.jfinal.core;

import com.jfinal.kit.StringKit;
import com.jfinal.plugin.activerecord.ActiveRecordException;
import com.jfinal.plugin.activerecord.Model;
import com.jfinal.plugin.activerecord.TableInfo;
import com.jfinal.plugin.activerecord.TableInfoMapping;
import com.jfinal.sog.kit.StringPool;

import javax.servlet.http.HttpServletRequest;
import java.lang.reflect.Method;
import java.util.Map;
import java.util.Map.Entry;

/**
 * ModelInjector
 */
final class ModelInjector {

    public static <T> T inject(Class<?> modelClass, HttpServletRequest request, boolean skipConvertError) {
        String modelName = modelClass.getSimpleName();
        return inject(modelClass, StringKit.firstCharToLowerCase(modelName), request, skipConvertError);
    }

    @SuppressWarnings({"rawtypes", "unchecked"})
    public static <T> T inject(Class<?> modelClass, String modelName, HttpServletRequest request, boolean skipConvertError) {
        Object model;
        try {
            model = modelClass.newInstance();
        } catch (Exception e) {
            throw new RuntimeException(e);
        }

        if (model instanceof Model)
            injectActiveRecordModel((Model) model, modelName, request, skipConvertError);
        else
            injectCommonModel(model, modelName, request, modelClass, skipConvertError);

        return (T) model;
    }

    private static void injectCommonModel(Object model, String modelName, HttpServletRequest request, Class<?> modelClass, boolean skipConvertError) {
        Method[] methods = modelClass.getMethods();
        for (Method method : methods) {
            String methodName = method.getName();
            if (!methodName.startsWith("set"))    // only setter method
                continue;

            Class<?>[] types = method.getParameterTypes();
            if (types.length != 1)                        // only one parameter
                continue;

            String attrName = methodName.substring(3);
            String value = request.getParameter(modelName + StringPool.DOT + StringKit.firstCharToLowerCase(attrName));
            if (value != null) {
                try {
                    method.invoke(model, TypeConverter.convert(types[0], value));
                } catch (Exception e) {
                    if (!skipConvertError)
                        throw new RuntimeException(e);
                }
            }
        }
    }

    @SuppressWarnings("rawtypes")
    private static void injectActiveRecordModel(Model<?> model, String modelName, HttpServletRequest request, boolean skipConvertError) {
        TableInfo tableInfo = TableInfoMapping.me().getTableInfo(model.getClass());

        String modelNameAndDot = modelName + StringPool.DOT;

        Map<String, String[]> parasMap = request.getParameterMap();
        for (Entry<String, String[]> e : parasMap.entrySet()) {
            String paraKey = e.getKey();
            if (paraKey.startsWith(modelNameAndDot)) {
                String paraName = paraKey.substring(modelNameAndDot.length());
                Class colType = tableInfo.getColType(paraName);
                if (colType == null)
                    throw new ActiveRecordException("The model attribute " + paraKey + " is not exists.");
                String[] paraValue = e.getValue();
                try {
                    // Object value = Converter.convert(colType, paraValue != null ? paraValue[0] : null);
                    Object value = paraValue[0] != null ? TypeConverter.convert(colType, paraValue[0]) : null;
                    model.set(paraName, value);
                } catch (Exception ex) {
                    if (!skipConvertError)
                        throw new RuntimeException("Can not convert parameter: " + modelNameAndDot + paraName, ex);
                }
            }
        }
    }
}

